package cz.possoft.messages;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Managing configuration and localization properties
 * 
 * @author mposolda
 */
public class PropertiesManager {
	
	private static PropertiesManager singleton = new PropertiesManager();
	
	private Properties confProperties;
	private ResourceBundle localizationBundle;
	
	// map of bundles for each game. When game is deployed (undeployed), then new version of bunlde should be put to map. 
	private Map<String, ResourceBundle> specificLocalizationBundles = new ConcurrentHashMap<String, ResourceBundle>();
	
	private PropertiesManager() {
		initializeProperties();
	}
	
	public static PropertiesManager getInstance() {
		return singleton;
	}
	
	/**
	 * Get configuration property from main bundle configuration.properties
	 */
	public String getConfProperty(String key) {
	   	return confProperties.getProperty(key);
	}
	
	/**
	 * Get message for specific key from localized resource bundle. Key is taken from main properties file messages.properties in main lib "PossoftNetClient".
	 */
	public String getMessageForKey(String key) {
		return getMessageFromBundleSafe(localizationBundle, key);
	}
	
	/**
	 * Get message for specific key from localized resource bundle. Key is taken from properties file messages.properties in client lib given by prefix keyOfLib.
	 * keyOfLib should be something like "Chess", "Four" etc. 
	 */	
	public String getMessageForKey(String key, String keyOfLib) {
		ResourceBundle bundle = specificLocalizationBundles.get(keyOfLib);
		String result = null;
		if (bundle != null) {
			result = getMessageFromBundleSafe(bundle, key);
		}
		// try to get from base bundle in case that key is not in specific bundle.
		if (key.equals(result)) {
			result = getMessageForKey(key);
		}
		return result;
	}
	
	/**
	 * Getting of parameterized message from bundle and replace sequences like {0} with param values. 
	 * If keyOfLib is null, then messages are taken from default resource bundle.
	 */
	public String getParameterizedMessageForKey(String key, String keyOfLib, Object... params) {
		String result = null;
		result = keyOfLib==null ? getMessageForKey(key) : getMessageForKey(key, keyOfLib);
		return new ParamsPropertyReplacer(result, params).getFinalString();
	}
	
	/**
	 * This is safe version for getting message from bundle. If message is not found for given key in given bundle, then value of key is returned.
	 * No exception is thrown.
	 */
	private static String getMessageFromBundleSafe(ResourceBundle bundle, String key) {
		String result = null;
		try {
			result = bundle.getString(key);
		}
		catch (MissingResourceException mre) {
			result = key;
		}
		return result;
	}
	
	/**
	 * Called during deployment of specific game. Bundle from specific deployment is loaded by this.
	 */
	public void handleDeployment(String key, ClassLoader loader) {
   		String locale = confProperties.getProperty("conf.locale");
   		ResourceBundle bundle = ResourceBundle.getBundle("specific/messages", new Locale(locale), loader);
   		specificLocalizationBundles.put(key, bundle);
	}
	
	/**
	 * Called during undeployment of specific game. Bundle from specific deployment is unloaded by this.
	 */
	public void handleUndeployment(String key) {
   		specificLocalizationBundles.remove(key);
	}	
	    
	private void initializeProperties() {
	   	try {
	   		confProperties = getProperties("configuration.properties");
	   		String locale = confProperties.getProperty("conf.locale");
	   		if (locale == null) {
	   			throw new IllegalStateException("Can't load property \"conf.locale\" from properties file.");
	   		}
	   		localizationBundle = ResourceBundle.getBundle("messages", new Locale(locale));
	   	}
	   	catch (IOException ioe) {
	   		System.err.println("Can't load properties file.");
	   		ioe.printStackTrace();
	   	}
	}
	    
	private Properties getProperties(String resource) throws IOException {
		InputStream is = new BufferedInputStream(new FileInputStream(resource));
		Properties props = new Properties();
		props.load(is);
		return props;
	}	
	
	/**
	 * Handle properties like "This is property with param {0} which needs tro be changed"
	 */
	private class ParamsPropertyReplacer {
		
		String result = null;
		
		private ParamsPropertyReplacer(String propertyValue, Object... paramValues) {
			result = propertyValue;
			for (int i=0; i<paramValues.length ; i++) {
				String toReplace = "{" + i + "}";
				result = result.replace(toReplace, paramValues[i].toString());
			}
		}
		
		private String getFinalString() {
			return result;
		}
	}
		
}
